#pragma once

#include <QObject>
#include <QKeyEvent>
#include <stdint.h>
#include <unistd.h>

/*!
 * \brief 辅助连接宏参数 - 概念连接符：##操作符
 */
#define CONCAT_HELPER(x,y)   x ## y
#define CONCAT(x,y)          CONCAT_HELPER(x, y)
#define CONCAT2(_1,_2)       CONCAT(_1, _2)
#define CONCAT3(_1,_2,_3)    CONCAT(CONCAT(_1, _2), _3)
#define CONCAT4(_1,_2,_3,_4) CONCAT(CONCAT3(_1, _2, _3), _4)
#define STR_HELPER(x)        #x
#define STR(x)               STR_HELPER(x)

/*!
 * \brief 结构变量中一个成员的地址找到这个结构体变量的首地址
 */
//  __extension__：消除非标准警告
//	C链表操作中场景是这样的：
//  获取到一个元素A(或结构体)地址，而它又是另一个结构体B的成员, 使用这个函数可以通过A地址求取B结构体地址
// 		ptr：指向功能成员的指针，存放功能成员的地址
// 		type：宿主结构体的类型
// 		member：功能成员在结构体中的表示（变量名）
// 	1.定义一个变量的格式是： 修饰符+变量类型+变量名 = 右值；
// 		修饰符            变量类型                        变量名        右值
// 		const     typeof( ((type*)0)->member )    *__mptr =  (ptr) ;
//    	“typeof( ((type*)0)->member )”代表的是一种数据类型：
// 		((type*)0)：它把0转换为一个type类型（也就是宿主结构体类型）
// 		((type*)0)->member：这个0指针指向结构体中的member成员
// 		typeof是gcc的c语言扩展保留字，用于获取变量的类型
// 		typeof( ((type*)0)->member )*：得出member的数据类型
//	2.(char *)__mptr - offset(type, member)：
//		用第2行获得的结构体成员地址减去其在结构体中的偏移值便可得到宿主结构体的地址
// 		将__mptr转换为char类型指针：C语言中，一个指向特定数据类型的指针减1，实际上减去的是它所指向数据类型的字节大小sizeof(data)
//		(type*)( (char *)__mptr - offset(type, member) ) 最后把地址转换成宿主结构体的类型type
#define container_of(ptr, type, member)  ( __extension__ ({     \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );}))

/*!
 * \brief 工具类
 */
class Utils : public QObject {
    Q_OBJECT
public:
    /*!
     * \brief generateRandomString 生成随机字符串
     * \param length 字符串长度
     * \return 字符串
     */
    static const QString generateRandomString(const int &length);

    static const unsigned char* generateRandomUnsignedChar(unsigned char *randomUnsignedChar, const int &length);
    /*!
     * \brief hasAdminPrivileges 获取是否具有管理员权限
     * \return 是否具有管理员权限
     */
    //static const bool hasAdminPrivileges() { return getuid() == 0 || geteuid() == 0; }
    /*!
     * \brief executeCommand 执行Shell命令
     * \param command 命令
     * \param arguments 参数
     * \param inputData 输入数据
     * \param dataSize 数据大小
     * \return 命令响应
     */
    static const QString executeCommand(const QString &command, const QStringList& arguments, \
                                        const void *inputData = nullptr, const int dataSize = 0);
    /*!
     * \brief checkAdmin 检查设备是否存在分区
     * \return 设备是否存在分区
     */
    Q_INVOKABLE static const bool checkDevicePartitionsExist(const QString &path);
    /*!
     * \brief Utils::checkFileOccupancy 检查文件/目录是否占用
     * \param path 文件/目录路径
     * \return 是否占用
     */
    static const bool checkFileOccupancy(const QString &path);
    /*!
     * \brief getFileSize 获取文件大小
     * \param path 文件路径
     * \return 文件大小
     */
    Q_INVOKABLE static const int getFileSize(const QString &path);
    /*!
     * \brief getFilesystemInfo 获取文件系统信息
     * \param path 文件系统挂载路径
     * \return 文件系统信息
     * {"AvailableSize":"14.7 GB","Device":"/dev/mapper/auditcrypt6", \
     *  "Percent":0.6596181670534599,"TotalSize":"14.8 GB","Type":"vfat"}
     */
    Q_INVOKABLE static const QVariantMap getFilesystemInfo(const QString &path);

    Q_INVOKABLE static const QString getDeviceMountpoint(const QString &path);

    static void remountRoFilesystem(const QString &path);

    static void remountRWFilesystem(const QString &path);
    /*!
     * \brief umountDeviceFilesystem 卸载设备文件系统
     * \param path 设备路径
     * \return 卸载状态
     */
    Q_INVOKABLE static const bool umountDeviceFilesystem(const QString &path);
    /*!
     * \brief openFileBrowser 打开文件浏览器
     * \param path 路径
     * \return 打开状态
     */
    Q_INVOKABLE static const bool openFileBrowser(const QString &path);

    Q_INVOKABLE static const bool isAutoStart();

    Q_INVOKABLE static const bool enableAutoStart(bool enable);
};
